﻿using Advantech.Motion;
using DLLPC.Utilities;
using HalconDotNet;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;
using PCBase.UIControls;
using System.ComponentModel;
using System.IO;

namespace VisionCore
{
    public class LaserHelper : KeyenceHelper
    {
        private string _IP;
        private int _Port;
        private Thread thread;
        private SystemTimeCounter sysTimer = new SystemTimeCounter();

        public double CycleTime { get; set; } = 0;

        private bool enable = true;
        public bool Enable
        {
            get { return enable; }
        }
        private bool connected = false;
        public bool Connected
        {
            get { return connected; }
        }

        public string retData = "";
        public string RetData
        {
            get { return retData; }
        }

        public bool IsLaserPowerCheck = false;
        public LaserPowerCheck LaserPowerCheckPara { get; set; } = new LaserPowerCheck();
        public double LaserPowerCheckValue { get; set; } = 0;
        public List<BlockInfo> Blocks { get; set; } = new List<BlockInfo>();

        /// <summary>
        /// 准备状态 
        /// 0:就绪ON 
        /// 1:发生错误 
        /// 2：刻印或展开
        /// </summary>
        public int LaserReadyStatus { get; set; } = -1;
        public int ProgramNo_Cur { get; set; } = 0;
        /// <summary>
        /// Laser刻印完成
        /// </summary>
        public bool MarkingComp { get; set; } = false;

        public LaserHelper(string strIP, int port)
        {
            _IP = strIP;
            _Port = port;
            CallBackEventHandler += new Action<CtrlCMD, string>(CallBackFunc);

            for (int i = 0; i < 255; i++)
            {
                BlockInfo info = new BlockInfo();
                info.Idx = i;
                Blocks.Add(info);
            }
        }

        public void Start(bool en = true)
        {
            enable = en;
            thread = new Thread(theadFunction);
            thread.IsBackground = true;
            thread.Start();
        }

        public void Quit()
        {
            IsRun = false;
        }

        private PollStatus StatusCode = new PollStatus();
        private void theadFunction()
        {
            if (!Enable) return;
            StatusCode = PollStatus.Open;
            sysTimer.Start();
            while (IsRun)
            {
                switch (StatusCode)
                {
                    case PollStatus.Open:
                        if (Connect(_IP, _Port) == false)
                        {
                            Thread.Sleep(4000);
                        }
                        else
                        {
                            StatusCode = PollStatus.Initial;
                            connected = true;
                            LogMsg("连接成功！！！");
                        }
                        break;
                    case PollStatus.Initial:
                        if (InitialFunction() == false)
                        {
                            StatusCode = PollStatus.Error;
                        }
                        else
                        {
                            StatusCode = PollStatus.Polling;
                        }
                        break;
                    case PollStatus.Polling:
                        if (PollingFunction() == false)
                        {
                            StatusCode = PollStatus.Error;
                            connected = false;
                        }
                        break;
                    case PollStatus.Error:
                        ErrorHandle();
                        StatusCode = PollStatus.Open;
                        break;
                    default:
                        break;
                }

                IsError = (StatusCode == PollStatus.Polling ? false : true);

                sysTimer.Stop();
                CycleTime = sysTimer.Duration;
                sysTimer.Start();

                SystemTimeCounter.timeBeginPeriod(1);
                Thread.Sleep(1);
                SystemTimeCounter.timeEndPeriod(1);
            }
            DisConnect();
        }

        private void CallBackFunc(CtrlCMD cmd, string strMsgRec)
        {
            int position = strMsgRec.IndexOf("\r");
            string resMsg = strMsgRec.Substring(0, position);
            retData = resMsg;

            if (resMsg.Length < 5) return;

            string ret = resMsg.Substring(3, 2);
            if (ret == "OK")
            {
                if (resMsg.Substring(0, 2) == "RX")
                {
                    switch (cmd.Name)
                    {
                        case "Ready":
                            {
                                string retData = resMsg.Substring(position - 1, 1);
                                LaserReadyStatus = int.Parse(retData);
                            }
                            break;
                        case "Error":
                            {
                                string retData = resMsg.Substring(6, 1);
                                if (retData == "1")
                                {
                                    LogMsg("Error:" + resMsg.Substring(8, position - 8));
                                }
                            }
                            break;
                        case "ProgramNo":
                            {
                                string retData = resMsg.Substring(6, position - 6);
                                ProgramNo_Cur = int.Parse(retData);
                            }
                            break;
                        case "BlockNo":
                            {
                                string retData = resMsg.Substring(6, position - 6);
                                int temp1 = retData.IndexOf(",");
                                Blocks[cmd.BlockNo].CharacterString = retData.Substring(0, temp1);

                                string retData1 = retData.Substring(temp1+1, retData.Length - temp1-1);
                                int temp2 = retData1.IndexOf(",");
                                Blocks[cmd.BlockNo].X = double.Parse(retData1.Substring(0, temp2));

                                string retData2 = retData1.Substring(temp2+1, retData1.Length - temp2-1);
                                int temp3 = retData2.IndexOf(",");
                                Blocks[cmd.BlockNo].Y = double.Parse(retData2.Substring(0, temp3));
                                Blocks[cmd.BlockNo].Z = double.Parse(retData2.Substring(temp3+1, retData2.Length - temp3-1));
                            }
                            break;
                        case "MarkingParameter":
                            {
                                string retData = resMsg.Substring(6, position - 6);
                                int temp1 = retData.IndexOf(",");
                                Blocks[cmd.BlockNo].MarkingParameter.LaserPower = double.Parse(retData.Substring(0, temp1));

                                string retData1 = retData.Substring(temp1 + 1, retData.Length - temp1 - 1);
                                int temp2 = retData1.IndexOf(",");
                                Blocks[cmd.BlockNo].MarkingParameter.LaserSpeed = uint.Parse(retData1.Substring(0, temp2));

                                string retData2 = retData1.Substring(temp2 + 1, retData1.Length - temp2 - 1);
                                int temp3 = retData2.IndexOf(",");
                                Blocks[cmd.BlockNo].MarkingParameter.LaserFrequency = uint.Parse(retData2.Substring(0, temp3));

                                string retData3 = retData2.Substring(temp3 + 1, retData2.Length - temp3 - 1);
                                int temp4 = retData3.IndexOf(",");
                                Blocks[cmd.BlockNo].MarkingParameter.LaserPointChange = retData3.Substring(0, temp4);
                                Blocks[cmd.BlockNo].MarkingParameter.LaserTime = uint.Parse(retData3.Substring(temp4 + 1, retData3.Length - temp4 - 1));
                            }
                            break;
                    }
                }
                else if (resMsg.Substring(0, 2) == "WX")
                {
                    if (cmd.Name == "StartMarking")
                    {
                        MarkingComp = true;
                    }
                    else if (cmd.Name == "LaserPowerCheck")
                    {
                        string retData = resMsg.Substring(6, position - 6);
                        LaserPowerCheckValue = double.Parse(retData);
                        Task.Run(() =>
                        {
                            WriteLog(string.Format("设定参数:{0}%,{1}kHz 测量结果:{2}", LaserPowerCheckPara.LaserPower,LaserPowerCheckPara.LaserFrequency,LaserPowerCheckValue), DateTime.Now.ToString("yyyy-MM-dd"));
                        });
                        IsLaserPowerCheck = false;
                    }
                }
            }
            else
            {
                if (resMsg.Substring(0, 2) == "WX")
                {
                    LogMsg("错误响应:" + resMsg.Substring(7, position - 7));
                }
            }
        }

        private void GetReadyStatus()
        {
            LaserReadyStatus = -1;
            SQ_SendCMD.Enqueue(new CtrlCMD(CtrlCMD.typeMode.Ready));
        }

        private void GetMarkingParam(int num, int blockNo)
        {
            Blocks[blockNo].MarkingParameter.LaserSpeed = 0;
            Blocks[blockNo].MarkingParameter.LaserPower = 0;
            Blocks[blockNo].MarkingParameter.LaserFrequency = 0;
            Blocks[blockNo].MarkingParameter.LaserPointChange = "0000";
            Blocks[blockNo].MarkingParameter.LaserTime = 0;
            string cmd = string.Format("PRG={0},BLK={1},MarkingParameter", num, blockNo); ;
            SQ_SendCMD.Enqueue(new CtrlCMD(CtrlCMD.typeMode.MarkingParameter, cmd, blockNo));
        }
        private void GetProgramNo()
        {
            ProgramNo_Cur = -1;
            SQ_SendCMD.Enqueue(new CtrlCMD(CtrlCMD.typeMode.ProgramNo));
        }

        private void GetBlockNoParam(int blockNo)
        {
            string cmd = string.Format("BLK={0},CharacterString,BlockPosition", blockNo); ;
            SQ_SendCMD.Enqueue(new CtrlCMD(CtrlCMD.typeMode.BlockNoParam, cmd, blockNo));
        }

        private object lockObject_LaserPowerCheck = new object();
        /// <summary>
        /// 激光功率测量
        /// </summary>
        /// <param name="infos"></param>
        public void LaserPowerCheck(LaserPowerCheck infos)
        {
            if (infos == null) return;
            LaserPowerCheckValue = 0;
            lock (lockObject_LaserPowerCheck)
            {
                Task.Run(() =>
                {
                    string cmd = string.Format("{0},{1}", infos.LaserPower, infos.LaserFrequency);
                    SQ_SendCMD.Enqueue(new CtrlCMD(CtrlCMD.typeMode.LaserPowerCheck, cmd));
                    IsLaserPowerCheck = true;
                    Task.Delay(10000);
                });
            }
        }



        private object lockObject_SetMarkingPara = new object();
        public int BlockNo = 0;
        /// <summary>
        /// 设置刻印条件
        /// </summary>
        /// <param name="infos"></param>
        public void SetMarkingParams(int num,List<BlockInfo> infos,MarkingParameter Markinginfos)
        {
            if (num < 0 || num > 1999|| infos == null || infos.Count == 0 || Markinginfos == null) return;

            lock (lockObject_SetMarkingPara)
            {
                Task.Run(() =>
                {
                    for (int i = 0; i < infos.Count; i++)
                    {
                        if (infos[i].Idx < 0 || infos[i].Idx > 255) continue;
                        BlockNo=infos[i].Idx;   
                        string cmd = string.Format("PRG={0},BLK={1},MarkingParameter={2},{3},{4},{5},{6}", num,infos[i].Idx,Markinginfos.LaserPower, Markinginfos.LaserSpeed, Markinginfos.LaserFrequency, Markinginfos.LaserPointChange, Markinginfos.LaserTime);
                        SQ_SendCMD.Enqueue(new CtrlCMD(CtrlCMD.typeMode.WX, cmd));
                    }

                    Task.Delay(500);

                    for (int i = 0; i < infos.Count; i++)
                    {
                        if (infos[i].Idx < 0 || infos[i].Idx > 255) continue;
                        GetMarkingParam(num, infos[i].Idx);
                        Task.Delay(500);
                    }

                });
            }
        }

        private object lockObject_compareMarkingPara = new object();
        /// <summary>
        /// 刻印条件比对
        /// </summary>
        /// <param name="infos"></param>
        public bool CompareMarkingParams(List<BlockInfo> infos, MarkingParameter Markinginfos)
        {
            if (infos == null || infos.Count == 0 || Markinginfos == null)
            {
                LogMsg("刻印条件比对失败00");
                return false;
            }


            for (int i = 0; i < infos.Count; i++)
            {
                if (Blocks.FindIndex(e => e.Idx == infos[i].Idx) >= infos[i].Idx)
                {
                    BlockInfo info = Blocks.Find(e => e.Idx == infos[i].Idx);
                    if (!(Markinginfos.LaserPower == info.MarkingParameter.LaserPower && Markinginfos.LaserSpeed == info.MarkingParameter.LaserSpeed && Markinginfos.LaserFrequency == info.MarkingParameter.LaserFrequency && Markinginfos.LaserPointChange == info.MarkingParameter.LaserPointChange && Markinginfos.LaserTime == info.MarkingParameter.LaserTime))
                    {
                        LogMsg("刻印条件比对NG01");
                        return false;
                    }
                }
                else
                {
                    LogMsg("刻印条件比对NG02");
                    return false;
                }
            }
            LogMsg("刻印条件比对OK");
            return true;
        }

        private object lockObject_SetProg = new object();
        /// <summary>
        /// 设置程序编号
        /// </summary>
        /// <param name="num"></param>
        public void SetProgramNo(int num)
        {
            lock (lockObject_SetProg)
            {
                Task.Run(() =>
                {
                    if (num < 0 || num > 1999) return;

                    string cmd = string.Format("ProgramNo={0}", num);
                    SQ_SendCMD.Enqueue(new CtrlCMD(CtrlCMD.typeMode.WX, cmd));

                    Task.Delay(100);
                    GetProgramNo();
                });
            }
        }

        private object lockObject_SetPara = new object();
        /// <summary>
        /// 设置信息组参数
        /// </summary>
        /// <param name="infos"></param>
        public void SetBlockNoParams(List<BlockInfo> infos)
        {
            if (infos == null || infos.Count == 0) return;

            lock (lockObject_SetPara)
            {
                Task.Run(() =>
                {
                    for (int i = 0; i < infos.Count; i++)
                    {
                        if (infos[i].Idx < 0 || infos[i].Idx > 255) continue;

                        string cmd = string.Format("BLK={0},CharacterString={1},BlockPosition={2},{3},{4}", infos[i].Idx, infos[i].CharacterString, infos[i].X, infos[i].Y, infos[i].Z);
                        SQ_SendCMD.Enqueue(new CtrlCMD(CtrlCMD.typeMode.WX, cmd));
                    }

                    Task.Delay(500);

                    GetBlockParams(infos);
                });
            }
        }

        /// <summary>
        /// 信息组参数比对
        /// </summary>
        /// <param name="infos"></param>
        public bool CompareBlock(List<BlockInfo> infos)
        {
            if (infos == null || infos.Count == 0)
            {
                LogMsg("信息组参数比对失败");
                return false;
            }


            for (int i = 0; i < infos.Count; i++)
            {
                if (Blocks.FindIndex(e => e.Idx == infos[i].Idx) >= infos[i].Idx)
                {
                    BlockInfo info = Blocks.Find(e => e.Idx == infos[i].Idx);
                    if (!(info.CharacterString == infos[i].CharacterString&& info.X == infos[i].X&& info.Y == infos[i].Y))
                    {
                        LogMsg("信息组参数比对NG01");
                        return false;
                    }
                }
                else
                {
                    LogMsg("信息组参数比对NG02");
                    return false;
                }
            }
            LogMsg("信息组参数比对OK");
            return true;
        }

        private object lockObject_GetPara = new object();
        public void GetBlockParams(List<BlockInfo> infos)
        {
            if (infos == null || infos.Count == 0) return;

            lock (lockObject_GetPara)
            {
                Task.Run(() =>
                {
                    for (int i = 0; i < infos.Count; i++)
                    {
                        if (infos[i].Idx < 0 || infos[i].Idx > 255) continue;

                        GetBlockNoParam(infos[i].Idx);
                    }
                });
            }
        }

        private object lockObject_Start = new object();
        /// <summary>
        /// 开始刻印
        /// </summary>
        public void StartMarking()
        {
            lock (lockObject_Start)
            {
                Task.Run(() =>
                {
                  GetReadyStatus();

                  Task.Delay(100);

                //if (LaserReadyStatus == 0)
                //{
                    //string cmd = "StartMarking";
                    SQ_SendCMD.Enqueue(new CtrlCMD(CtrlCMD.typeMode.StartMarking));
                //}
                });
            }
        }

        /// <summary>
        /// 计算Die的信息组参数
        /// </summary>
        /// <param name="pos"></param>
        /// <param name="infos"></param>
        /// <returns></returns>
        public bool CalcLaserOffset(DieInfo dieInfo1 , PRM_LaserPosOffset.Laser_Point laserPosoffset, ref List<BlockInfo> infos)
        {
            if (infos == null || infos.Count == 0|| dieInfo1==null) return false;

            for (int i = 0; i < infos.Count; i++)
            {
                infos[i].Idx = ParameterHelper.CurParam.LaserParam.Blocks[i].Idx;                
                //infos[i].X = Math.Round(dieInfo1.LaserPoint.Laser.X - ParameterHelper.CurParam.Die.CodeBasePos_X + laserPosoffset.X, 3);
                //infos[i].Y = Math.Round(dieInfo1.LaserPoint.Laser.Y - ParameterHelper.CurParam.Die.CodeBasePos_Y + laserPosoffset.Y + ParameterHelper.CurParam.Die.Pitch*i,3);
                infos[i].Z = (Math.Round(dieInfo1.LaserPoint.Laser.Z,3) > 5|| Math.Round(dieInfo1.LaserPoint.Laser.Z, 3) <- 5) ? 0 : Math.Round(dieInfo1.LaserPoint.Laser.Z, 3);
                infos[i].Enable = true;
            }

            return true;
        }

        //public bool CalcLaserOffset(PRM_LaserPos.Laser_Point pos, ref List<BlockInfo> infos)
        //{
        //    if (infos == null || infos.Count == 0) return false;

        //    double xOffset = pos.X - ParameterHelper.CurParam.InspParam.LaserBase.X;
        //    double yOffset = pos.Y - ParameterHelper.CurParam.InspParam.LaserBase.Y;

        //    for (int i = 0; i < infos.Count; i++)
        //    {
        //        infos[i].Idx = ParameterHelper.CurParam.InspParam.Blocks[i].Idx;
        //        infos[i].X = ParameterHelper.CurParam.InspParam.Blocks[i].X + xOffset;
        //        infos[i].Y = ParameterHelper.CurParam.InspParam.Blocks[i].Y + yOffset;
        //    }

        //    return true;
        //}

        private void ErrorHandle()
        {
            LogMsg("通讯错误");
            Thread.Sleep(4000);
        }

        private void LogMsg(string msg)
        {
            string strMsg = string.Format("{0} {1}", "Keyence Laser", msg);
            LogHelper.Write(LogHelper.Module.Motion_Laser, strMsg);
        }

        /// <summary>
        /// Log记录
        /// </summary>
        /// <param name="str">记录内容</param>
        /// <param name="filename">文件名称</param>
        /// 
        private void WriteLog(string str, string filename)
        {
            string strlog = "量测时间:"+DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss ") + str +"W"+ "\r\n";
            string filePath = $"E:\\Log\\LaserPowerCheck";
            string FileName = string.Format("{0}.txt", filename.ToString());

            if (Directory.Exists(filePath) == false)
            {
                Directory.CreateDirectory(filePath);
            }
            if (File.Exists(filePath + string.Format(@"\{0}", FileName)) == false)
            {
                File.Create(filePath + string.Format(@"\{0}", FileName)).Close();//File.Create返回值是文件流，必须关闭，不关闭就是被占用中
            }
                        
            try
            {
                if (File.Exists(filePath + string.Format(@"\{0}", FileName)) == true)
                {
                    FileStream fs = new FileStream(filePath + string.Format(@"\{0}", FileName), FileMode.Append, FileAccess.Write);
                    //获取字节数组
                    byte[] data = System.Text.Encoding.Default.GetBytes(strlog);
                    //开始写入
                    fs.Write(data, 0, data.Length);
                    //清空缓冲区，关闭流
                    fs.Flush();
                    fs.Close();
                }
            }
            catch (Exception)
            {
                throw;
            }
        }
        internal enum PollStatus
        {
            Open = 0,

            Initial = 4,

            Polling = 10,

            Error = 30
        }
    }

    public class BlockInfo
    {
        public bool Enable { get; set; } = false;

        [DisplayName("01.信息组编号"), Description()]
        [TypeConverter(typeof(PropertiesExpandConverter))]
        /// <summary>
        /// Laser信息组编号
        /// </summary>
        public int Idx { get; set; } = 0;

        [DisplayName("02.信息组刻印内容"), Description()]
        /// <summary>
        /// Laser字符串内容
        /// </summary>
        public string CharacterString { get; set; } = "";

        [DisplayName("04.信息组刻印坐标X"), Description()]
        /// <summary>
        /// Laser坐标
        /// </summary>
        public double X { get; set; } = 0;

        [DisplayName("05.信息组刻印坐标Y"), Description()]
        /// <summary>
        /// Laser坐标
        /// </summary>
        public double Y { get; set; } = 0;
        [DisplayName("06.信息组刻印坐标Z"), Description()]
        /// <summary>
        /// Laser坐标
        /// </summary>
        public double Z { get; set; } = 0;

        /// <summary>
        /// 激光刻印条件
        /// </summary>
        [DisplayName("07.信息组刻印条件"), Description()]
        [TypeConverter(typeof(PropertiesExpandConverter))]
        public MarkingParameter MarkingParameter { get; set; } = new MarkingParameter();
    }
    public class MarkingParameter
    {

        private double _laserPower = 2.0;
        [DisplayName("01.激光功率(%)"), Description("激光功率(%):0.0-100.0")]
        /// <summary>
        /// 激光功率
        /// </summary>
        public double LaserPower
        {
            get { return _laserPower; }
            set
            {
                _laserPower = Math.Round(value, 1);
                if (_laserPower > 100.0)
                {
                    _laserPower = 100.0;
                }
                if (_laserPower < 0.0)
                {
                    _laserPower = 0.0;
                }
            }
        }

        private uint _laserSpeed = 40;
        [DisplayName("02.扫描速度(mm/s)"), Description("扫描速度(mm/s):1-12000")]

        /// <summary>
        /// 扫描速度
        /// </summary>
        public uint LaserSpeed
        {
            get { return _laserSpeed; }
            set
            {
                _laserSpeed = value;
                if (_laserSpeed > 12000)
                {
                    _laserSpeed = 12000;
                }
                if (_laserSpeed < 1)
                {
                    _laserSpeed = 1;
                }
            }
        }

        private uint _laserFrequency = 40;
        [DisplayName("03.脉冲频率(kHz)"), Description("脉冲频率(kHz):000~400")]

        /// <summary>
        /// 扫描速度
        /// </summary>
        public uint LaserFrequency
        {
            get { return _laserFrequency; }
            set
            {
                _laserFrequency = value;
                if (_laserFrequency > 400)
                {
                    _laserFrequency = 400;
                }
                if (_laserFrequency < 0)
                {
                    _laserFrequency = 0;
                }
            }
        }

        private string _laserPointChange = "0000";
        [DisplayName("04.光束点可变"), Description("光束点可变:210~0210")]

        /// <summary>
        /// 光束点可变
        /// </summary>
        public string LaserPointChange
        {
            get { return "0000"; }
            set
            {
                _laserPointChange = value;
            }
        }

        private uint _laserTime = 1;
        [DisplayName("05.刻印次数(次)"), Description("刻印次数(次):001~100")]

        /// <summary>
        /// 刻印次数
        /// </summary>
        public uint LaserTime
        {
            get { return _laserTime; }
            set
            {
                _laserTime = value;
                if (_laserTime > 100)
                {
                    _laserTime = 100;
                }
                if (_laserTime < 1)
                {
                    _laserTime = 1;
                }
            }
        }
    }
    public class LaserPowerCheck
    {

        private double _laserPower = 2.0;
        [DisplayName("01.激光功率(%)"), Description("激光功率(%):0.0-100.0")]
        /// <summary>
        /// 激光功率
        /// </summary>
        public double LaserPower
        {
            get { return Math.Round(_laserPower,1); }
            set
            {
                _laserPower = Math.Round(value, 1);
                if (_laserPower > 100.0)
                {
                    _laserPower = 100.0;
                }
                if (_laserPower < 0.0)
                {
                    _laserPower = 0.0;
                }
            }
        }


        private uint _laserFrequency = 40;
        [DisplayName("02.脉冲频率(kHz)"), Description("脉冲频率(kHz):000~400")]

        /// <summary>
        /// 扫描速度
        /// </summary>
        public uint LaserFrequency
        {
            get { return _laserFrequency; }
            set
            {
                _laserFrequency = value;
                if (_laserFrequency > 400)
                {
                    _laserFrequency = 400;
                }
                if (_laserFrequency < 0)
                {
                    _laserFrequency = 0;
                }
            }
        }

    }

}
